CRE:渲染总结概览
=>Application::UpdateScene()
=> if (updateWorld) PlayerLoop() // player loop
=> RenderManager::UpdateAllRenderers();
=> if !UNITY_EDITOR
=> if(!batchmode)PlayerRender()//player renderer
=> RenderManager::RenderOffscreenCameras();//所有离屏相机渲染
=> RenderManager::RenderCameras();//所有相机渲染
Camera的渲染
=>Camera.DoRender()
=>DoRendererLoop(RenderLoop& loop, RenderingPath renderPath, CullResults& contents, bool dontRenderRenderables)
//其中loop是Camera的成员RenderLoop* m_RenderLoop;
=>⭕foreach(node in VisibleNodes) //遍历所有Visible节点,加入到loop.m_Objects中
foreach(mat)
//fill rodata
=>⭕if(renderPath == kRenderPathPrePass)
DoPrePassRenderLoop()
=>⭕else if()
DoForwardShaderRenderLoop (loop.m_Context, loop.m_Objects[kPartOpaque], ...);//Opaque
=>⭕else if()
DoForwardShaderRenderLoop (loop.m_Context, loop.m_Objects[kPartAfterOpaque], ...);//AfterOpaque
=>ForwardVertexRenderLoop queue;//定义局部对象queue(ForwardShaderRenderLoop不继承RenderLoop)
=>设置queue.m_Objects(RenderObjectDataContainer)
=> queue.PerformRendering()
=>for(shadowmap in m_ShadowMaps)//所有阴影贴图 m_ShadowMaps
RenderLightShadowMaps(....);//渲染阴影贴图(使用casterBounds、Cascades等因素剔除)
=>for(pass)//遍历所有pass
=>RenderPassData& rpData = m_PlainRenderPasses[i];//Pass数据
=>RenderObjectData& roDataH = (*m_Objects)[rpData.roIndex];//pass对应的渲染对象
=>VisibleNode * node = roDataH.visibleNode; //每个pass只渲染一个node??
=> if(xxxx) m_BatchRenderer.Add(node->renderer, subsetindex, channels, node->worldMatrix, rs.transformType);//使用批处理器添加渲染对象。
=> if(xxx) renderer-Render() //渲染那些不适合批处理的对象。
剔除和几何排序流程概述
所有Renderers -> CullResults -> VisibleNodes -> Passes -> foreach(pass.node){直接Render/加入Batch}
几种渲染路径
enum RenderingPath {
kRenderPathVertex = 0, //对应Vertext Lit
kRenderPathForward,//对应前向渲染
kRenderPathPrePass,//对应延迟渲染
kRenderPathCount
};
阴影渲染
DoPrePass中或者DoForwardShaderRenderLoop都有渲染阴影的调用。
如果是kRenderPathPrePass(延迟渲染),就会在PrePass中渲染阴影。
相关类定义
class:
CullResults //剔除结果
VisibleNodes nodes;// VisibleNode的动态数组
...
class:
RenderLoop//渲染循环
RenderObjectDataContainer m_Objects[kPartCount];// 可能是真正的渲染队列 kPartCount有Opaque和AfterOpaque两种
...
struct VisibleNode : public TransformInfo
{
void SetTransformInfo(const TransformInfo& src) { TransformInfo::operator=(src); }
BaseRenderer* renderer;
float lodFade;
};
struct RenderObjectData {
Unity::Material* material; // 4
SInt16 queueIndex; // 2
UInt16 subsetIndex; // 2
SInt16 subShaderIndex; // 2
UInt16 sourceMaterialIndex;// 2
UInt16 lightmapIndex; // 2
int staticBatchIndex; // 4
float distance; // 4
//@TODO: cold?
float distanceAlongView; // 4
VisibleNode* visibleNode; // 4
Shader* shader; // 4 shader to use
GlobalLayeringData
globalLayeringData; // 4
// 36 bytes
};
如何选择要渲染的Renderer?
??Camera::CustomCull ??
??PrepareSceneCullingParameters » ??SceneCullParameters??实际的场景Renderer选取??
sceneCullParameters.renderers[kStaticRenderers].nodes = GetScene().GetStaticSceneNodes();
sceneCullParameters.renderers[kCameraIntermediate].nodes = cameraIntermediate.GetSceneNodes();
返回Scene::m_RendererNodes.begin()
??CullScene??
??PrepareSceneNodes??填充VisObjs??
Scene.m_RendererNodes来自何处?
Scene::AddRendererInternal(Renderer*)
Scene::AddRenderer
Renderer::UpdateRenderer ()
几何排序
//构造RenderLoop.m_Objects
void DoRenderLoop()//在DoRenderLoop中
{
//...
foreach(it in cullresults.nodes)
{
//...
foreach(int matidx in matCount)
{
//...
RenderObjectData& odata = loop.m_Objects[part].push_back ();
odata.xx = xxx;
odata.xx = xxx;
odata.xx = xxx;
//...
}
//...
}
//...
}
//对passes进行排序
queue.SortRenderPassData<true> (queue.m_PlainRenderPasses);
//排序函数
template <bool opaque>
void SortRenderPassData( RenderPasses& passes )
{
RenderObjectSorter<opaque> sorter;
sorter.queue = this;
std::sort( passes.begin(), passes.end(), sorter );
}
//排序器
template <bool opaque>
struct RenderObjectSorter
{
bool operator()( const RenderPassData& ra, const RenderPassData& rb ) const;
const ForwardShaderRenderLoop* queue;
};
//排序器实现(operator())中条件优先顺序排列:
layer和order
//全局layer和order
首通道标志 (kPackFirstPassFlag):
//优先处理首通道,这对于确保依赖于特定通道顺序的渲染操作(如阴影或基础光照计算)至关重要。
光照贴图索引 (lightmapIndex):
//由于相同光照贴图的对象渲染时可以共享资源,按此索引排序有助于减少光照贴图切换,提高渲染效率。
静态批处理索引 (staticBatchIndex):
//在启用批处理的情况下,按批处理索引排序,使得批处理的对象优先渲染,这样可以大幅度减少绘制调用,优化渲染性能。
材质索引 (sourceMaterialIndex):
//对于不属于任何批处理的对象,按照材质索引排序,以确保同一材质的对象连续渲染,减少材质切换次数。
着色器实例ID (shader->GetInstanceID()):
//通过按着色器实例排序,减少着色器程序的切换,可以优化GPU资源的使用。
材质实例ID (material->GetInstanceID()):
//类似于着色器,按材质实例排序也是为了减少材质切换,维持渲染管线的连贯性和效率。
渲染通道 (pass):
//最后,在同一材质和着色器内部,按渲染通道排序,确保同一材质的不同通道按照预定顺序执行,有助于正确实现复杂的材质效果。
//设置Pass然后渲染:
channels = rs.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex);
if (channels)
{
SetupObjectMatrix (node->worldMatrix, rs.transformType);
node->renderer->Render( subsetIndex, *channels );
}
//GPT: ChannelAssigns类负责管理顶点数据通道的绑定
//一般包括以下几类信息:
//顶点数据通道:指定如何从顶点缓冲区读取数据,例如位置、法线、纹理坐标、颜色等。
//顶点布局描述:定义了顶点数据的组织和格式,告诉渲染管线如何解释顶点缓存中的数据。
//纹理引用:包含哪些纹理需要被绑定到着色器中使用。
//纹理采样方式:如何对纹理进行采样,包括纹理过滤、包裹模式等。
//Uniforms/常量
//渲染状态信息
//Shader程序引用
class ChannelAssigns {
public:
// 构造函数
ChannelAssigns();
// 从解析的着色器通道配置中设置通道绑定
void FromParsedChannels (const ShaderLab::ParserBindChannels& parsed);
// 绑定一个源通道到指定的顶点组件
void Bind(ShaderChannel source, VertexComponent target);
// 解除一个顶点组件的绑定
void Unbind(VertexComponent target);
// 与另一个ChannelAssigns对象合并通道配置
void MergeWith(const ChannelAssigns& additional);
// 获取已配置的目标顶点组件的映射(哪些顶点组件已经被设置了数据源)
UInt32 GetTargetMap() const;
// 获取已使用的源通道的映射(哪些数据源通道已经被使用)
UInt32 GetSourceMap() const;
// 检查通道分配是否为空(没有任何通道绑定)
bool IsEmpty() const;
// 检查所有源通道是否直接映射到对应的目标组件,没有交叉连接(如src.TexCoord0 -> dst.TexCoord1)
bool IsDirectlyWired() const;
// 根据目标顶点组件获取其数据源的通道
ShaderChannel GetSourceForTarget(VertexComponent target) const;
// 比较两个ChannelAssigns对象是否相等
bool operator== (const ChannelAssigns& other) const;
private:
// 重新计算直接连接的状态
void RecalculateIsDirectlyWired();
private:
UInt32 m_TargetMap; // 表示哪些顶点组件已经被设置了数据源的位字段
UInt32 m_SourceMap; // 表示哪些数据源通道已经被使用的位字段
SInt8 m_Channels[kVertexCompCount]; // 每个顶点组件的数据源通道
bool m_DirectlyWired; // 所有源通道是否直接映射到对应的目标组件
// 序列化时使用的友元结构
friend struct GfxRet_ChannelAssigns;
};
//VertexComponent枚举
enum VertexComponent
{
kVertexCompNone = 0,
kVertexCompVertex,
kVertexCompColor,
kVertexCompNormal,
kVertexCompTexCoord,
kVertexCompTexCoord0, kVertexCompTexCoord1, kVertexCompTexCoord2, kVertexCompTexCoord3,
kVertexCompTexCoord4, kVertexCompTexCoord5, kVertexCompTexCoord6, kVertexCompTexCoord7,
kVertexCompAttrib0, kVertexCompAttrib1, kVertexCompAttrib2, kVertexCompAttrib3,
kVertexCompAttrib4, kVertexCompAttrib5, kVertexCompAttrib6, kVertexCompAttrib7,
kVertexCompAttrib8, kVertexCompAttrib9, kVertexCompAttrib10, kVertexCompAttrib11,
kVertexCompAttrib12, kVertexCompAttrib13, kVertexCompAttrib14, kVertexCompAttrib15,
kVertexCompCount // keep this last!
};
enum VertexChannelFormat
{
kChannelFormatFloat = 0,
kChannelFormatFloat16,
kChannelFormatColor,
kChannelFormatByte,
kChannelFormatCount
};
(END)